%% This may look like text, but it is really -*- LaTeX -*-
%
% $RCSfile: xbra.tex,v $
% $Revision: 1.3 $
% $Author: bammi $
%
%%
\documentstyle[12pt,fullpage,titlepage]{article}

\title{The XBRA Protocol}
\author{{\it Jwahar R. Bammi} \\ {\it bammi@cadence.com}}
\date{$Revision: 1.3 $}

\newlength{\interskip}
\setlength{\interskip}{0.5\baselineskip}
\addtolength{\interskip}{-.1\interskip}

\begin{document}
\maketitle

\addtolength{\parskip}{0.3\baselineskip}
\setlength{\parindent}{0pt}

\section{Introduction}
This document is an attempt to specify the XBRA protocol for exception
vector chaining on the atariST series of computers. It is a response
to many requests I have received to document the protocol, and also an
encouragement for people grabbing exception vectors to use it. This protocol
was not invented by me, nor is it perfect for every situation, but
still is very useful because something is better than nothing. There
have been other proposals, but IMHO this is the simplest and cleanest,
and does the job for which it was designed quite well. It has been
used quite widely, especially in Public Domain circles, and that is a
good enough reason for me to follow it.

\section{History}
Just to set the record straight, and give credit where it is due, the
original proposal for this protocol was made by Moshe Braner in an
Usenet article sometime in 1988 (or was it 87, I do not remember). The
proposal was worked over by Julian Reschke in consultation with
Edgar R\"oeder in Germany, where it ended up
in the present form.  Julian Reschke has since been its chief spokesman, and
clearing house for registering unique XBRA identifiers. Atari Germany adopted
XBRA as an ``official'' standard in 1989 but didn't do anything to push it.
XBRA as I know it, came
back to me from Germany in the form of an include file for the Gnu C
library, thanks to Edgar R\"oeder. In case you are wondering XBRA is an
abbreviation for e{\bf X}tended {\bf BRA}ner protocol.

\section{Why XBRA}
On the 68K architecture, whenever an exception occurs, the current
processor state\footnote{What and how much of the processor state is
saved varies with different models of the 68K family.}${}^,$\footnote{This
saved information is known as an Exception Frame.} is saved on the
supervisor stack (SSP), and then the processor effectively jumps
indirectly via a fixed location in memory to code that will handle
that exception.  This fixed location is known as the exception vector
for that particular exception. Exception vectors are memory locations
from which the processor fetches the address of the code that will
handle that exception when it occurs. Initially, all exception vectors
are initialized by TOS to point to handlers for the exceptions within
TOS.

Now suppose on an exception, your program needs to get control (say to
set a variable). A common practice is to ``hook your code into the
exception processing chain'', for the particular exception that your
program is interested in. What we mean by ``hooking your code in'' is
that you make the exception vector point to your code, which in turns
points to wherever the original exception vector was pointing to. When the
exception occurs, your code is given control, and once it has done its
job, control passes to other exception handlers in the chain. The last
handler in this chain (usually the one installed there initially by
TOS), finishes off the exception handling sequence by executing a RTE
instruction, which restores the processor to the state in which it was before
the exception occurred. Figure~\ref{fig:hooking} depicts what
happens when you ``hook in your code''.

\begin{figure}
\begin{center}
{\small%
\input fig1.tex
}
\caption{Hooking into an Exception Vector}
\label{fig:hooking}
\end{center}
\end{figure}

It is bad {\it karma} not to pass a control to the next exception
handler in the chain. Even if your code can completely handle an
exception, it denies other handlers the opportunity to do what they
supposed to do. Of course, there are situations where
passing control to other handlers is not appropriate (for instance if
you are intercepting a particular gemdos function
in order to replace it totally; in most other
cases you passing control on to the next handler is the thing to do).

When you install a handler, by convention you install it at the head
of the exception chain. But you cannot assume that your handler will
always remain there. Other processes can execute later and may insert
their own exception handlers. When it comes time to unhook
first you need to find yourself. This is where
XBRA comes into its own. If the exception vector (the head of the
chain) does not point to your handler there is no way you can find it,
unless all handlers between the head and you follow a common
conventions to identify the handler and to find the pointer to
the next element of this chain. The
XBRA protocol specifies where to find, relative to the beginning of a handler,
the next handler address. To avoid spurious identification
the XBRA protocol further defines two magic numbers, again at fixed
offsets relative to the handler.  Now unchaining yourself becomes easy
and reliable. You either encountered yourself, or you continue
down the chain by picking up the address of the next handler from a
fixed offset in the current handler.  Once you have found yourself
you check the two magic numbers, again at fixed offsets, to ensure
that you actually hit an XBRA header rather than some random memory
location that just happens to have the value that you are looking
for. If it all checks out, you can remove yourself reliably.
Figure~\ref{fig:xbrachain} illustrates a typical XBRA exception chain.

\begin{figure}
\begin{center}
{\small%
\input fig2.tex
}
\caption{An XBRA Exception Chain}
\label{fig:xbrachain}
\end{center}
\end{figure}

\pagebreak

\section{The XBRA Protocol}
A generic XBRA element looks like:
{\small%
\begin{center}
{\tt%
\begin{tabbing}
xxxx\=xxxxxxxxxxx\=xxxxxxxxxxxxxx\= \kill
struct xbra \{ \\
\>    long \>       xbra\_magic; \\
\>    long \>       my\_magic;   \\
\>    void \>       (*next)();    \\
\>    $\langle$ exception code here $\rangle$ \\
\};
\end{tabbing}}
\end{center}}

{\tt xbra\_magic} is initialized to {\tt ``XBRA''} (0x58425241L hex).
{\tt my\_magic} is a 32 bit value that you choose. In gcc compiled
applications a default value is {\tt ``GNUC''} (0x474E5543L hex)
but of course it may be changed. {\tt next} holds
the address of the next exception handler in the chain. It is
initialized when the handler is initially linked into the
chain, just past its head.
The code for your handler follows the {\tt next} field.
You will land at the address of the first word of this
code when control is passed to this handler.
Usually we do not put our handler right into the structure, but
instead we put a jump instruction to our handler routine in the XBRA
structure. This is a convenience for C programs and not a requirement of
the XBRA protocol. In C there is no portable way to ensure that the code for
the handler is contiguous to the XBRA header and in the same section (text or
data section)
 Here is an example of how to install a handler into the
event timer handoff vector (exception 0x100).
{\small%
\begin{verbatim}
void my_handler()
{
  /* handler code */
}

#define _XBRA_MAGIC     0x58425241L /* "XBRA" */
#define _GNUC_MAGIC     0x474E5543L /* "GNUC" */
#define _JMP_OPCODE     0x4EF9
#define _TIME_VEC       0x100

struct __xbra {
   long             xbra_magic;
   long             gnu_magic;
   void             (*next)();
   short            jump;
   void             (*handler_address)();
} xbra = { _XBRA_MAGIC,  /* XBRA magic                     */
           _GNUC_MAGIC,  /* application magic              */
           0L,           /* pointer to next handler        */
           _JMP_OPCODE,  /* jump instruction               */
           my_handler    /* address of our handler routine */
         };

void install_handler()
{
    xbra.next = Setexc(_TIME_VEC, &xbra.jump);
    /*
     * Setexc returns the address of the old handler that we stash
     * away in xbra.next. In its place we put the address of the
     * jump instruction in the XBRA structure. When the processor
     * picks up this address, it executes the jump instruction to
     * the entry point of our handler function.
     */
}
\end{verbatim}}

After our handler is installed, when the exception occurs it finally
gets control. It must do what it needs to, then branch to the next
exception handler in the chain (after it has cleaned up anything it
put on the stack)\footnote{GNUC users will have to be careful if using the
\mbox{-fomit-frame-pointer} option.}.  Please note that a piece
of an assembler code in the example below is using gcc syntax.
A keyword {\tt volatile} is needed as a guard against an overzealous
optimizer.

{\small%
\begin{verbatim}
void my_handler()
{
  /* my handler code goes here */
  __asm__ volatile("unlk a6          /* clean up stack */
                    movl _xbra+8, a0 /* address of next handler in A0 */
}                   jmp  a0@");      /* jump to it */
\end{verbatim}}

To unlink our handler from the exception chain we first need to
locate it in memory, and then make its predecessor point to its
successor. This is where the XBRA protocol really comes into its own.
We begin the search at the exception vector looking for our XBRA
elements address. If our address is found we authenticate it
by testing the two magic numbers. Otherwise
we travel down the chain by following the XBRA elements
{\tt next} pointer.


{\small%
\begin{verbatim}
/*
 * unlink a handler in a xbra friendly manner from the exc chain
 */
static void unlink_handler(me, exc)
xbra_struct *me;
int exc;
{
    xbra_struct *this, *prev;
    long save_ssp;
    
    this = (xbra_struct *)      /* get head of chain */
        ((unsigned long)Setexc(exc, -1L) - offsetof(xbra_struct, jump));
    if(this == me)
    {   /* at the head, just unlink */
        Setexc(exc, me->next);
        return;
    }
    /* otherwise find me in the chain and unlink */
    save_ssp = Super(0L);
    for(prev = this; this && (this != me); prev = this,
        this = (xbra_struct *)((this->next)
             ? (((char *)(this->next)) - offsetof(xbra_struct, jump)) : 0))
    {
        /* validate the xbra */
        if(this->xbra_magic != _XBRA_MAGIC) 
        {       /* a non XBRA handler in the chain -- shame on you */
            Super(save_ssp);
            Setexc(exc, me->next); /* nuke it, otherwise it may call ME */
            return;                /* after I am deinstalled */
        }
    }
    
    if( (this == me) && (this->gnuc_magic == _GNUC_MAGIC) )
    {   /* unlink me from middle of the chain */
        prev->next = this->next;
        Super(save_ssp);
        return;
    }
    /* we are screwed */
    Super(save_ssp);
    Cconws("\r\nHandler not found!\r\n\n");
}

/* this routine may be called as unlink_handler(&xbra, _TIME_VEC) */
\end{verbatim}}

\section{Registering your XBRA}
To be fully XBRA compatible, your unique XBRA id must meet the following
criteria:
\begin{itemize}
  \item The id should contain only printable ASCII characters (32..126).
  \item The id has to be registered with Julian Reschke to ensure that
        it is unique. To do this you need to send mail to 
	\mbox{julian@math.uni-muenster.de} or \mbox{jr@ms.maus.de} containing
	the following information:
  \begin{itemize}
	  \item The full program name.
	  \item Name of the author.
	  \item XBRA id.
	  \item Program status (PD, shareware, commercially and available
	         from...).
	  \item A list of used vectors.
  \end{itemize}
\end{itemize}
If you are unable to send internet mail, i'll be glad to pass on your
request to Julian. I may be contacted on GEnie as \mbox{J.Bammi}
or on CompuServe as \mbox{71515,155}.

\section{Risks}
There are two major risks in using the XBRA protocol as described above:
\begin{itemize}
  \item Putting code in the data section.
  \item Overzealous use of vector grabbing.
\end{itemize}
As described above we put a jump instruction in the XBRA structure that points
to our exception handler. Normally, C compilers will allocate this structure
in the data section, so when control is passed to our handler, an instruction
will be fetched from the data section. Normally, this does not cause problems
on the atariST/TT architectures beacuse there is no protection.
But, if protection was ever enforced by TOS
we would be in trouble, and we would end up with a segmentation violation as 
code would be fetched from a a non-text section. Putting  the XBRA structure
in the text section is worse. Since we patch in the next address, it would
be akin to self modifying code. On the 68030/40 machines this will not work
if the instruction cache is read-only. For now, putting the header in the data
section seems to be the most reasonable and portable solution. But be aware
that on the 030/040 you may have to force a cache flush after changing contents
to patch the next address, otherwise the system will crash.

On the subject of overzealous useage of XBRA Allan Pratt had the following 
words of advise:\\
``It's just that I would have big banners all over it saying that
vector stealing is a bad idea in the first place.  There are
just a few vectors that you can legally steal, and a few more that
you can reasonably steal.  I would use only legal vectors
and legal uses in the examples.  One kind of legal use for
a legal vector is a RAMdisk using hdv\_rw (and \_bpb and \_mediach).
Also, talk about unhooking vectors needs a whole raft of warnings
about what a bad idea this is.

The right way to do almost anything that involves vector stealing is to
use a TSR in the AUTO folder as a driver, and have a foreground
program communicate with that driver, usually through a trap it's
stealing or through the cookie the driver left in the cookie jar.
Then there's never a question of unhooking.  The driver is generally
small enough that you don't mind having it around even when you're
not using the program in question.

I guess I would just like to see more in the way of caveats, so people
won't read this document and think that vector stealing is the best way
to do something.  It often isn't.  But that's all gravy on top of the
actual stated goal of the document, which is to describe XBRA itself.''

\section{Conclusion}
We have tried to document the XBRA protocol, and to illustrate its usage
with some typical code segments. Of course, your application may vary
in many details. The XBRA protocol does a good job of
reliably locating handlers in an exception chain and properly
installing and removing them from the chain. A complete example is
presented in Appendix A. The {\tt xbra.h} include file from the GNUC
library is presented in Appendix B.
\clearpage
\appendix
\section{A complete example}
{\footnotesize%
\begin{verbatim}
/*
 * XBRA example: install a handler using XBRA protocol into TOSs
 * timer handoff vector. Sit in a loop displaying a counter value
 * until any key is hit. Then remove the handler in a XBRA friendly
 * manner.
 *
 *      ++jrb    bammi@cadence.com
 */
#include <stddef.h>
#include <xbra.h>
#include <osbind.h>
#include <stdio.h>

unsigned short counter = 0;     /* counter incremented in handler */
unsigned short divisor = 50;    /* increment counter every divisor ticks */
void my_handler(void);
xbra_struct tick_xbra = _XBRA_INIT(my_handler); /* XBRA element */

#define _TIME_VEC       0x100

/*
 * our exception handler 
 */
void my_handler(void)
{
    if(--divisor == 0) 
    {
        counter++;  divisor = 50;
    }
    __asm__ volatile
    ("  unlk    a6                  /* clean up stack frame */
        movl    _tick_xbra+8, a0    /* jump to next handler */
        jmp     a0@");
}
        
/*
 * unlink a handler in a xbra friendly manner from the exc chain
 * returns 0 on clean unlink, !0 if something if it could'nt
 */
int unlink_handler(me, exc)
xbra_struct *me;
int exc;
{
    xbra_struct *this, *prev;
    long save_ssp;
    
    this = (xbra_struct *)      /* get head of chain */
        ((unsigned long)Setexc(exc, -1L) - offsetof(xbra_struct, jump));
    if(this == me)
    {   /* at the head, just unlink */
        Setexc(exc, me->next);
        return 0;
    }
    /* otherwise find me in the chain and unlink */
    save_ssp = Super(0L);
    for(prev = this; this && (this != me); prev = this,
        this = (xbra_struct *)((this->next) ?
                (((char *)(this->next)) - offsetof(xbra_struct, jump)) : 0))
    {
        /* validate the xbra */
        if(this->xbra_magic != _XBRA_MAGIC) 
        {       /* shame on you */
            Super(save_ssp);
            Setexc(exc, me->next); /* nuke it, otherwise it may call ME */
            return 1;              /* after I am deinstalled */
        }
    }
    
    if( (this == me) && (this->gnuc_magic == _GNUC_MAGIC) )
    {   /* unlink me from middle of the chain */
        prev->next = this->next;
        Super(save_ssp);
        return 0;
    }
    /* we are screwed */
    Super(save_ssp);
    return 2;
}

int main()
{
    char out[8];
    unsigned short last = 0xffff;

    /* install our handler */
    tick_xbra.next = (xptr) Setexc(_TIME_VEC, _XBRA_VEC(tick_xbra));

    /* continuously display counter until a key is hit */
    do {
        if(last != counter)
        {
            last = counter;
            sprintf(out, "%05u\r", last); /* display counter */
            Cconws(out);
        }
    } while( Bconstat(2) == 0 );

    /* remove the handler */
    return unlink_handler(&tick_xbra, _TIME_VEC);
}
\end{verbatim}}
\clearpage
\section{GNUC xbra.h}
{\footnotesize%
\begin{verbatim}
#ifndef _XBRA_H
#define _XBRA_H

typedef void (*xptr) __PROTO((void));

typedef struct __xbra
{
  long        xbra_magic;
  long        gnuc_magic;
  xptr        next;
  short        jump;
  void        (*this) __PROTO((void));
} xbra_struct;

#define _XBRA_MAGIC        0x58425241L /* "XBRA" */
#define _GNUC_MAGIC        0x474E5543L /* "GNUC" */
#define _JMP_OPCODE        0x4EF9
#define _JSR_OPCODE        0x4EB9

#define _XBRA_VEC(x)        ((void (*) __PROTO((void))) &(x.jump))

#define _XBRA_INIT(fn)  {                  \
                           _XBRA_MAGIC,    \
                           _GNUC_MAGIC,    \
                           (xptr) 0,       \
                           _JMP_OPCODE,    \
                           (void (*) __PROTO((void))) fn \
                        }

#define _TRAP_VEC(n,x) \
 ((void (*) __PROTO((void))) (((long) n << 24) | (long) &(x.jump)))

#define _TRAP_INIT(fn)  {                  \
                           _XBRA_MAGIC,    \
                           _GNUC_MAGIC,    \
                           (xptr) 0,       \
                           _JSR_OPCODE,    \
                           (void (*) __PROTO((void))) fn \
                        }
#endif /* _XBRA_H */
\end{verbatim}}

\end{document}

